{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "name": "videoProcessor.ipynb",
      "provenance": [],
      "collapsed_sections": [
        "HRjmZ9FD4mAg"
      ],
      "include_colab_link": true
    },
    "kernelspec": {
      "name": "python3",
      "display_name": "Python 3"
    },
    "accelerator": "GPU"
  },
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "view-in-github",
        "colab_type": "text"
      },
      "source": [
        "<a href=\"https://colab.research.google.com/github/3dsf/SkinDeep/blob/master/videoProcessor.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Y8AgUL1FgGEq"
      },
      "source": [
        "# Video Processing with AI models\n",
        "based on the ffmpeg-python tensorflow implementation \n",
        "and using the models of \n",
        "https://github.com/vijishmadhavan\n",
        "\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "HRjmZ9FD4mAg"
      },
      "source": [
        "# Install Libs"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "K8YPFvvMVVvx",
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "outputId": "6a4b14ef-2898-4cb7-eb7a-269d51279635"
      },
      "source": [
        "!pip install youtube-dl fastai==1.0.61 ffmpeg-python"
      ],
      "execution_count": 1,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Requirement already satisfied: youtube-dl in /usr/local/lib/python3.7/dist-packages (2021.4.26)\n",
            "Requirement already satisfied: fastai==1.0.61 in /usr/local/lib/python3.7/dist-packages (1.0.61)\n",
            "Requirement already satisfied: ffmpeg-python in /usr/local/lib/python3.7/dist-packages (0.2.0)\n",
            "Requirement already satisfied: Pillow in /usr/local/lib/python3.7/dist-packages (from fastai==1.0.61) (7.1.2)\n",
            "Requirement already satisfied: numpy>=1.15 in /usr/local/lib/python3.7/dist-packages (from fastai==1.0.61) (1.19.5)\n",
            "Requirement already satisfied: nvidia-ml-py3 in /usr/local/lib/python3.7/dist-packages (from fastai==1.0.61) (7.352.0)\n",
            "Requirement already satisfied: pandas in /usr/local/lib/python3.7/dist-packages (from fastai==1.0.61) (1.1.5)\n",
            "Requirement already satisfied: packaging in /usr/local/lib/python3.7/dist-packages (from fastai==1.0.61) (20.9)\n",
            "Requirement already satisfied: requests in /usr/local/lib/python3.7/dist-packages (from fastai==1.0.61) (2.23.0)\n",
            "Requirement already satisfied: fastprogress>=0.2.1 in /usr/local/lib/python3.7/dist-packages (from fastai==1.0.61) (1.0.0)\n",
            "Requirement already satisfied: numexpr in /usr/local/lib/python3.7/dist-packages (from fastai==1.0.61) (2.7.3)\n",
            "Requirement already satisfied: beautifulsoup4 in /usr/local/lib/python3.7/dist-packages (from fastai==1.0.61) (4.6.3)\n",
            "Requirement already satisfied: pyyaml in /usr/local/lib/python3.7/dist-packages (from fastai==1.0.61) (3.13)\n",
            "Requirement already satisfied: scipy in /usr/local/lib/python3.7/dist-packages (from fastai==1.0.61) (1.4.1)\n",
            "Requirement already satisfied: torch>=1.0.0 in /usr/local/lib/python3.7/dist-packages (from fastai==1.0.61) (1.8.1+cu101)\n",
            "Requirement already satisfied: torchvision in /usr/local/lib/python3.7/dist-packages (from fastai==1.0.61) (0.9.1+cu101)\n",
            "Requirement already satisfied: spacy>=2.0.18; python_version < \"3.8\" in /usr/local/lib/python3.7/dist-packages (from fastai==1.0.61) (2.2.4)\n",
            "Requirement already satisfied: matplotlib in /usr/local/lib/python3.7/dist-packages (from fastai==1.0.61) (3.2.2)\n",
            "Requirement already satisfied: bottleneck in /usr/local/lib/python3.7/dist-packages (from fastai==1.0.61) (1.3.2)\n",
            "Requirement already satisfied: future in /usr/local/lib/python3.7/dist-packages (from ffmpeg-python) (0.16.0)\n",
            "Requirement already satisfied: pytz>=2017.2 in /usr/local/lib/python3.7/dist-packages (from pandas->fastai==1.0.61) (2018.9)\n",
            "Requirement already satisfied: python-dateutil>=2.7.3 in /usr/local/lib/python3.7/dist-packages (from pandas->fastai==1.0.61) (2.8.1)\n",
            "Requirement already satisfied: pyparsing>=2.0.2 in /usr/local/lib/python3.7/dist-packages (from packaging->fastai==1.0.61) (2.4.7)\n",
            "Requirement already satisfied: chardet<4,>=3.0.2 in /usr/local/lib/python3.7/dist-packages (from requests->fastai==1.0.61) (3.0.4)\n",
            "Requirement already satisfied: idna<3,>=2.5 in /usr/local/lib/python3.7/dist-packages (from requests->fastai==1.0.61) (2.10)\n",
            "Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.7/dist-packages (from requests->fastai==1.0.61) (2020.12.5)\n",
            "Requirement already satisfied: urllib3!=1.25.0,!=1.25.1,<1.26,>=1.21.1 in /usr/local/lib/python3.7/dist-packages (from requests->fastai==1.0.61) (1.24.3)\n",
            "Requirement already satisfied: typing-extensions in /usr/local/lib/python3.7/dist-packages (from torch>=1.0.0->fastai==1.0.61) (3.7.4.3)\n",
            "Requirement already satisfied: thinc==7.4.0 in /usr/local/lib/python3.7/dist-packages (from spacy>=2.0.18; python_version < \"3.8\"->fastai==1.0.61) (7.4.0)\n",
            "Requirement already satisfied: tqdm<5.0.0,>=4.38.0 in /usr/local/lib/python3.7/dist-packages (from spacy>=2.0.18; python_version < \"3.8\"->fastai==1.0.61) (4.41.1)\n",
            "Requirement already satisfied: murmurhash<1.1.0,>=0.28.0 in /usr/local/lib/python3.7/dist-packages (from spacy>=2.0.18; python_version < \"3.8\"->fastai==1.0.61) (1.0.5)\n",
            "Requirement already satisfied: srsly<1.1.0,>=1.0.2 in /usr/local/lib/python3.7/dist-packages (from spacy>=2.0.18; python_version < \"3.8\"->fastai==1.0.61) (1.0.5)\n",
            "Requirement already satisfied: plac<1.2.0,>=0.9.6 in /usr/local/lib/python3.7/dist-packages (from spacy>=2.0.18; python_version < \"3.8\"->fastai==1.0.61) (1.1.3)\n",
            "Requirement already satisfied: wasabi<1.1.0,>=0.4.0 in /usr/local/lib/python3.7/dist-packages (from spacy>=2.0.18; python_version < \"3.8\"->fastai==1.0.61) (0.8.2)\n",
            "Requirement already satisfied: setuptools in /usr/local/lib/python3.7/dist-packages (from spacy>=2.0.18; python_version < \"3.8\"->fastai==1.0.61) (56.0.0)\n",
            "Requirement already satisfied: cymem<2.1.0,>=2.0.2 in /usr/local/lib/python3.7/dist-packages (from spacy>=2.0.18; python_version < \"3.8\"->fastai==1.0.61) (2.0.5)\n",
            "Requirement already satisfied: blis<0.5.0,>=0.4.0 in /usr/local/lib/python3.7/dist-packages (from spacy>=2.0.18; python_version < \"3.8\"->fastai==1.0.61) (0.4.1)\n",
            "Requirement already satisfied: catalogue<1.1.0,>=0.0.7 in /usr/local/lib/python3.7/dist-packages (from spacy>=2.0.18; python_version < \"3.8\"->fastai==1.0.61) (1.0.0)\n",
            "Requirement already satisfied: preshed<3.1.0,>=3.0.2 in /usr/local/lib/python3.7/dist-packages (from spacy>=2.0.18; python_version < \"3.8\"->fastai==1.0.61) (3.0.5)\n",
            "Requirement already satisfied: kiwisolver>=1.0.1 in /usr/local/lib/python3.7/dist-packages (from matplotlib->fastai==1.0.61) (1.3.1)\n",
            "Requirement already satisfied: cycler>=0.10 in /usr/local/lib/python3.7/dist-packages (from matplotlib->fastai==1.0.61) (0.10.0)\n",
            "Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.7/dist-packages (from python-dateutil>=2.7.3->pandas->fastai==1.0.61) (1.15.0)\n",
            "Requirement already satisfied: importlib-metadata>=0.20; python_version < \"3.8\" in /usr/local/lib/python3.7/dist-packages (from catalogue<1.1.0,>=0.0.7->spacy>=2.0.18; python_version < \"3.8\"->fastai==1.0.61) (3.10.1)\n",
            "Requirement already satisfied: zipp>=0.5 in /usr/local/lib/python3.7/dist-packages (from importlib-metadata>=0.20; python_version < \"3.8\"->catalogue<1.1.0,>=0.0.7->spacy>=2.0.18; python_version < \"3.8\"->fastai==1.0.61) (3.4.1)\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Yt5OjMJxEcmz"
      },
      "source": [
        "# Select\n",
        "## -- Model\n",
        "## -- Video\n",
        "## -- Output Name\n",
        "*and collect metadata*\n",
        " "
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "XOGWZjslqwGG",
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "outputId": "3bc64d66-f301-4cb4-fc0e-7cb552a502f1"
      },
      "source": [
        "import os\n",
        "\n",
        "modelToRun = \"SkinDeep_1280.pkl\" #@param [\"ArtLine_500.pkl\", \"ArtLine_650.pkl\", \"ArtLine_1024.pkl\", \"SkinDeep.pkl\", \"SkinDeep_1280.pkl\"]\n",
        "pathToModel = os.path.join(\"/content/drive/\",modelToRun)\n",
        "\n",
        "downloadModel = {\n",
        "    \"ArtLine_500.pkl\": \"https://www.dropbox.com/s/p9lynpwygjmeed2/ArtLine_500.pkl\",\n",
        "    \"ArtLine_650.pkl\": \"https://www.dropbox.com/s/starqc9qd2e1lg1/ArtLine_650.pkl\",\n",
        "    \"ArtLine_1024.pkl\": \"https://www.dropbox.com/s/rq90q9lr9arwdp8/ArtLine_1024%20%281%29.pkl\",\n",
        "    \"SkinDeep.pkl\": \"https://www.dropbox.com/s/5mmcqao4mozpube/SkinDeep.pkl?dl=1\",\n",
        "    \"SkinDeep_1280.pkl\": \"https://www.dropbox.com/s/wxty56nhidusojr/SkinDeep_1280.pkl\"\n",
        "}\n",
        "if os.path.isfile(pathToModel) == False : \n",
        "    if os.path.isfile(modelToRun) == False :\n",
        "        print(\"Downloading Model\")\n",
        "        download = downloadModel[modelToRun]\n",
        "        !wget -O $modelToRun $download\n",
        "        pathToModel = modelToRun\n",
        "    else :\n",
        "        print(\"Found Local Version\")\n",
        "        pathToModel = modelToRun\n",
        "\n",
        "videoURL = \"https://www.youtube.com/watch?v=olnqoL-yLZE\" #@param {type:\"string\"}\n",
        "!rm input.mp4  #required\n",
        "!time(youtube-dl -f 'bestvideo[ext=mp4]+bestaudio[ext=acc]/mp4' --output \"input.%(ext)s\"  $videoURL)\n",
        "output_name = \"postMalone.sd1280.mp4\" #@param {type:\"string\"}\n",
        "\n",
        "import subprocess\n",
        "AUDIO = False\n",
        "process = subprocess.Popen(['ffmpeg', '-hide_banner', '-i', 'input.mp4', '-y' ], stdout=subprocess.PIPE, stderr=subprocess.STDOUT,universal_newlines=True)\n",
        "for line in process.stdout:\n",
        "    print(line)\n",
        "    if ' Video:' in line:\n",
        "        l_split = line.split(',')\n",
        "        #print('---------printing line \", line)\n",
        "        for segment in l_split[1:]:\n",
        "            if 'fps' in segment:\n",
        "                    s = segment.strip().split(' ')\n",
        "                    fps = float(s[0])\n",
        "            if 'x' in segment:\n",
        "                    s = segment.strip().split('x')\n",
        "                    width = int(s[0])\n",
        "                    s2 = s[1].split(' ')\n",
        "                    height = int(s2[0])\n",
        "    if 'Duration:' in line:\n",
        "        s = line.split(',')\n",
        "        ss = s[0].split(' ')\n",
        "        sss = ss[3].strip().split(':')\n",
        "        seconds = float(sss[0])*60*60 + float(sss[1])*60 + float(sss[2])\n",
        "    if 'Audio:' in line:\n",
        "        AUDIO = True\n",
        "\n",
        "print('fps = ', str(fps))\n",
        "print('width = ', str(width))\n",
        "print('height = ', str(height))\n",
        "print('seconds = ', str(seconds))\n",
        "print('AUDIO = ', AUDIO)"
      ],
      "execution_count": 15,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Found Local Version\n",
            "[youtube] olnqoL-yLZE: Downloading webpage\n",
            "[download] Destination: input.mp4\n",
            "\u001b[K[download] 100% of 18.65MiB in 00:03\n",
            "\n",
            "real\t0m4.751s\n",
            "user\t0m0.761s\n",
            "sys\t0m0.075s\n",
            "Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'input.mp4':\n",
            "\n",
            "  Metadata:\n",
            "\n",
            "    major_brand     : mp42\n",
            "\n",
            "    minor_version   : 0\n",
            "\n",
            "    compatible_brands: isommp42\n",
            "\n",
            "    creation_time   : 2018-10-11T20:21:03.000000Z\n",
            "\n",
            "  Duration: 00:02:26.40, start: 0.000000, bitrate: 1068 kb/s\n",
            "\n",
            "    Stream #0:0(und): Video: h264 (Main) (avc1 / 0x31637661), yuv420p(tv, bt709), 1280x720 [SAR 1:1 DAR 16:9], 938 kb/s, 23.98 fps, 23.98 tbr, 90k tbn, 47.95 tbc (default)\n",
            "\n",
            "    Metadata:\n",
            "\n",
            "      creation_time   : 2018-10-11T20:21:03.000000Z\n",
            "\n",
            "      handler_name    : ISO Media file produced by Google Inc. Created on: 10/11/2018.\n",
            "\n",
            "    Stream #0:1(eng): Audio: aac (LC) (mp4a / 0x6134706D), 44100 Hz, stereo, fltp, 125 kb/s (default)\n",
            "\n",
            "    Metadata:\n",
            "\n",
            "      creation_time   : 2018-10-11T20:21:03.000000Z\n",
            "\n",
            "      handler_name    : ISO Media file produced by Google Inc. Created on: 10/11/2018.\n",
            "\n",
            "At least one output file must be specified\n",
            "\n",
            "fps =  23.98\n",
            "width =  1280\n",
            "height =  720\n",
            "seconds =  146.4\n",
            "AUDIO =  True\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "UWywo0Pageln"
      },
      "source": [
        "# Process Video"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "IcuaaaJYW3ip",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 293
        },
        "outputId": "af0bd8dd-4c8d-4105-f8cd-737494ca9ba3"
      },
      "source": [
        "import os\n",
        "import logging as logger\n",
        "\n",
        "from torchvision import transforms as T\n",
        "from fastai.utils.mem import *\n",
        "from fastai.vision import open_image, load_learner, Image, torch, pil2tensor, image2np\n",
        "\n",
        "import ffmpeg, cv2\n",
        "import numpy as np\n",
        "\n",
        "#progress bar\n",
        "from IPython.display import HTML, display\n",
        "from tqdm import *\n",
        "\n",
        "#There is scaling warning that might come up, and this block supresses user warnings\n",
        "#Comment out this block if your don't mind seeing the warnings\n",
        "import warnings\n",
        "warnings.filterwarnings(\"ignore\", category=UserWarning)  \n",
        "\n",
        "### Progress bar\n",
        "def progress(value, max=100):\n",
        "    return HTML(\"\"\"\n",
        "        <progress\n",
        "            value='{value}'\n",
        "            max='{max}',\n",
        "            style='width: 100%'\n",
        "        >\n",
        "            {value}\n",
        "        </progress>\n",
        "    \"\"\".format(value=value, max=max))\n",
        "\n",
        "### Class required for model\n",
        "class FeatureLoss(nn.Module):\n",
        "    def __init__(self, m_feat, layer_ids, layer_wgts):\n",
        "        super().__init__()\n",
        "        self.m_feat = m_feat\n",
        "        self.loss_features = [self.m_feat[i] for i in layer_ids]\n",
        "        self.hooks = hook_outputs(self.loss_features, detach=False)\n",
        "        self.wgts = layer_wgts\n",
        "        self.metric_names = ['pixel',] + [f'feat_{i}' for i in range(len(layer_ids))\n",
        "              ] + [f'gram_{i}' for i in range(len(layer_ids))]\n",
        "\n",
        "    def make_features(self, x, clone=False):\n",
        "        self.m_feat(x)\n",
        "        return [(o.clone() if clone else o) for o in self.hooks.stored]\n",
        "\n",
        "    def forward(self, input, target):\n",
        "        out_feat = self.make_features(target, clone=True)\n",
        "        in_feat = self.make_features(input)\n",
        "        self.feat_losses = [base_loss(input,target)]\n",
        "        self.feat_losses += [base_loss(f_in, f_out)*w\n",
        "                             for f_in, f_out, w in zip(in_feat, out_feat, self.wgts)]\n",
        "        self.feat_losses += [base_loss(gram_matrix(f_in), gram_matrix(f_out))*w**2 * 5e3\n",
        "                             for f_in, f_out, w in zip(in_feat, out_feat, self.wgts)]\n",
        "        self.metrics = dict(zip(self.metric_names, self.feat_losses))\n",
        "        return sum(self.feat_losses)\n",
        "\n",
        "    def __del__(self): self.hooks.remove()\n",
        "\n",
        "### DETERMINE IF CUDA AVAILABLE and LOAD MODEL\n",
        "def modelDeviceLoadSelect():  \n",
        "    if torch.cuda.is_available():\n",
        "        def load_model():\n",
        "            global USEgPU\n",
        "            learn = load_learner('.', pathToModel, device=0 )\n",
        "            USEgPU = True\n",
        "            print(\"INFERENCE DEVICE : cuda\")\n",
        "            return learn\n",
        "    else:\n",
        "        def load_model():\n",
        "            learn = load_learner('.', pathToModel, device='cpu')\n",
        "            print(\"INFERENCE DEVICE : cpu\")\n",
        "            return learn\n",
        "    learn=load_model()\n",
        "    return learn\n",
        "\n",
        "### Functions based on ffmpeg-python video tensorflow example\n",
        "def readFrameAsNp(ffmpegDecode, width, height):\n",
        "    logger.debug('Reading frame')\n",
        "\n",
        "    # Note: RGB24 == 3 bytes per pixel.\n",
        "    frame_size = width * height * 3\n",
        "    in_bytes = ffmpegDecode.stdout.read(frame_size)\n",
        "    if len(in_bytes) == 0:\n",
        "        frame = None\n",
        "    else:\n",
        "        assert len(in_bytes) == frame_size\n",
        "        frame = (\n",
        "            np\n",
        "            .frombuffer(in_bytes, np.uint8)\n",
        "            .reshape([height, width, 3])\n",
        "        )\n",
        "    return frame\n",
        "\n",
        "def writeFrameAsByte(ffmpegEncode, frame):\n",
        "    logger.debug('Writing frame')\n",
        "    ffmpegEncode.stdin.write(\n",
        "        frame\n",
        "        .astype(np.uint8)\n",
        "        .tobytes()\n",
        "    )\n",
        "\n",
        "def vid2np(in_filename):\n",
        "    logger.info('vid2np() -- Decoding to pipe')\n",
        "    codec = 'h264'\n",
        "    args = (\n",
        "            ffmpeg\n",
        "            .input(in_filename,\n",
        "                **{'c:v': codec})\n",
        "            .output('pipe:', format='rawvideo', pix_fmt='rgb24')\n",
        "            .global_args(\"-hide_banner\")\n",
        "            .compile()\n",
        "    )\n",
        "    return subprocess.Popen(args, stdout=subprocess.PIPE)\n",
        "\n",
        "def np2vid(out_filename, fps_out, in_file, widthOut, heightOut):\n",
        "    logger.info('np2vid() encoding from pipe')\n",
        "    global AUDIO\n",
        "    codec = 'h264'\n",
        "    if AUDIO == True :\n",
        "        pipeline2 = ffmpeg.input(in_file)\n",
        "        audio = pipeline2.audio\n",
        "        args = (\n",
        "            ffmpeg\n",
        "            .input('pipe:', format='rawvideo', pix_fmt='rgb24',\n",
        "                s='{}x{}'.format(widthOut, heightOut),\n",
        "                framerate=fps_out )\n",
        "            .output(audio, out_filename , pix_fmt='yuv420p', **{'c:v': codec}, \n",
        "                shortest=None, acodec='copy')\n",
        "            .global_args(\"-hide_banner\")\n",
        "            .overwrite_output()\n",
        "            .compile()\n",
        "        )\n",
        "    else:\n",
        "        args = (\n",
        "            ffmpeg\n",
        "            .input('pipe:', format='rawvideo', pix_fmt='rgb24', \n",
        "                s='{}x{}'.format(widthOut, heightOut), \n",
        "                framerate=fps_out )\n",
        "            .output(out_filename , pix_fmt='yuv420p', **{'c:v': codec})\n",
        "            .global_args(\"-hide_banner\")\n",
        "            .overwrite_output()\n",
        "            .compile()\n",
        "        )\n",
        "    return subprocess.Popen(args, stdin=subprocess.PIPE)\n",
        "\n",
        "### The model changes the resolution, processes blank to find new resolution\n",
        "def getOutputResolution():\n",
        "    #process a blank frame and return dimesions\n",
        "    blank = np.zeros([height,width,3],dtype=np.uint8)\n",
        "    blank.fill(255)\n",
        "    fastAI_image = Image(pil2tensor(blank, dtype=np.float32).div_(255))\n",
        "    p,img_hr,b = learn.predict(fastAI_image)\n",
        "    im = image2np(img_hr)\n",
        "    x = im.shape\n",
        "    out_height = x[0]\n",
        "    out_width = x[1]\n",
        "    return int(out_width), int(out_height)\n",
        "\n",
        "### This is where all the magic happens\n",
        "def processFrame(frame) :\n",
        "    global INCR\n",
        "    ### Frame comes in as np array\n",
        "    #Load image in fastai's framework as an image\n",
        "    fastAI_image = Image(pil2tensor(frame, dtype=np.float32).div_(255))\n",
        "    # Inference\n",
        "    p,img_hr,b = learn.predict(fastAI_image)\n",
        "    # Convert output tensor into np array\n",
        "    im = image2np(img_hr)\n",
        "    # alpha and beta control line output darkness / warmness\n",
        "    norm_image = cv2.normalize(im, None, alpha=-60, beta=260, norm_type=cv2.NORM_MINMAX, dtype=cv2.CV_8U)\n",
        "    INCR += 1\n",
        "    # enabling the next 2 lines will also output images when processing videos\n",
        "    #outCV2 = cv2.cvtColor(norm_image, cv2.COLOR_RGB2BGR )\n",
        "    #cv2.imwrite(output_name+ str(INCR) + \".png\", outCV2)  # INCR is just a frame counter\n",
        "    return norm_image \n",
        "\n",
        "if __name__ == '__main__':\n",
        "\n",
        "    INCR = 0\n",
        "\n",
        "    learn = modelDeviceLoadSelect()\n",
        "    outWidth, outHeight = getOutputResolution()\n",
        "    estimatedFrames = fps * seconds\n",
        "\n",
        "    print('Model = ', pathToModel)\n",
        "    print('*** Video In***')\n",
        "    print('fps = ', str(fps))\n",
        "    print('width = ', str(width))\n",
        "    print('height = ', str(height))\n",
        "    print('seconds = ', str(seconds))\n",
        "    print('AUDIO = ', AUDIO)\n",
        "    print()\n",
        "    print('*** Video Out***')\n",
        "    print('outWidth = ', str(outWidth))\n",
        "    print('outHeight = ', str(outHeight))\n",
        "    print('output_name = ', output_name)\n",
        "    print()\n",
        "    #progress bar\n",
        "    print('estimatedFrames = ', estimatedFrames)\n",
        "    out = display(progress(0, 100), display_id=True)\n",
        "\n",
        "    inputVid = 'input.mp4'\n",
        "    ffmpegDecode = vid2np(inputVid)\n",
        "    ffmpegEncode = np2vid(output_name, fps, inputVid, outWidth, outHeight)\n",
        "\n",
        "    while True:\n",
        "        timeMark = time.process_time()\n",
        "        in_frame = readFrameAsNp(ffmpegDecode, width, height)\n",
        "        if in_frame is None:\n",
        "            logger.info('End of input stream')\n",
        "            break\n",
        "\n",
        "        logger.debug('Processing frame')\n",
        "        out_frame = processFrame(in_frame)\n",
        "        writeFrameAsByte(ffmpegEncode, out_frame)\n",
        "\n",
        "        #progress bar\n",
        "        out.update(progress(INCR, estimatedFrames))\n",
        "        minutesRemaining = str(round((estimatedFrames-INCR)*(time.process_time()-timeMark)/60))\n",
        "        print(\"\\rEstimated Minutes Remaining = \", minutesRemaining, end=\"\")\n",
        "\n",
        "    logger.info('Waiting for ffmpegDecode')\n",
        "    ffmpegDecode.wait()\n",
        "\n",
        "    logger.info('Waiting for ffmpegEncode')\n",
        "    ffmpegEncode.stdin.close()\n",
        "    ffmpegEncode.wait()\n",
        "\n",
        "    logger.info('Done')"
      ],
      "execution_count": 16,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "INFERENCE DEVICE : cuda\n",
            "Model =  SkinDeep_1280.pkl\n",
            "*** Video In***\n",
            "fps =  23.98\n",
            "width =  1280\n",
            "height =  720\n",
            "seconds =  146.4\n",
            "AUDIO =  True\n",
            "\n",
            "*** Video Out***\n",
            "outWidth =  1280\n",
            "outHeight =  720\n",
            "output_name =  postMalone.sd1280.mp4\n",
            "\n",
            "estimatedFrames =  3510.672\n"
          ],
          "name": "stdout"
        },
        {
          "output_type": "display_data",
          "data": {
            "text/html": [
              "\n",
              "        <progress\n",
              "            value='3510'\n",
              "            max='3510.672',\n",
              "            style='width: 100%'\n",
              "        >\n",
              "            3510\n",
              "        </progress>\n",
              "    "
            ],
            "text/plain": [
              "<IPython.core.display.HTML object>"
            ]
          },
          "metadata": {
            "tags": []
          }
        },
        {
          "output_type": "stream",
          "text": [
            "Estimated Minutes Remaining =  0"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "JBsMMC0h-k-Q"
      },
      "source": [
        "# Download result"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "HFnYWciQ-qMy",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 17
        },
        "outputId": "635c58e2-cddc-4e80-e6a0-02ceaaa07e0e"
      },
      "source": [
        "from google.colab import files\n",
        "\n",
        "files.download(output_name)"
      ],
      "execution_count": 17,
      "outputs": [
        {
          "output_type": "display_data",
          "data": {
            "application/javascript": [
              "\n",
              "    async function download(id, filename, size) {\n",
              "      if (!google.colab.kernel.accessAllowed) {\n",
              "        return;\n",
              "      }\n",
              "      const div = document.createElement('div');\n",
              "      const label = document.createElement('label');\n",
              "      label.textContent = `Downloading \"${filename}\": `;\n",
              "      div.appendChild(label);\n",
              "      const progress = document.createElement('progress');\n",
              "      progress.max = size;\n",
              "      div.appendChild(progress);\n",
              "      document.body.appendChild(div);\n",
              "\n",
              "      const buffers = [];\n",
              "      let downloaded = 0;\n",
              "\n",
              "      const channel = await google.colab.kernel.comms.open(id);\n",
              "      // Send a message to notify the kernel that we're ready.\n",
              "      channel.send({})\n",
              "\n",
              "      for await (const message of channel.messages) {\n",
              "        // Send a message to notify the kernel that we're ready.\n",
              "        channel.send({})\n",
              "        if (message.buffers) {\n",
              "          for (const buffer of message.buffers) {\n",
              "            buffers.push(buffer);\n",
              "            downloaded += buffer.byteLength;\n",
              "            progress.value = downloaded;\n",
              "          }\n",
              "        }\n",
              "      }\n",
              "      const blob = new Blob(buffers, {type: 'application/binary'});\n",
              "      const a = document.createElement('a');\n",
              "      a.href = window.URL.createObjectURL(blob);\n",
              "      a.download = filename;\n",
              "      div.appendChild(a);\n",
              "      a.click();\n",
              "      div.remove();\n",
              "    }\n",
              "  "
            ],
            "text/plain": [
              "<IPython.core.display.Javascript object>"
            ]
          },
          "metadata": {
            "tags": []
          }
        },
        {
          "output_type": "display_data",
          "data": {
            "application/javascript": [
              "download(\"download_8ecc28a4-9bd4-48a7-a5d6-f7b2bab99820\", \"postMalone.sd1280.mp4\", 20561796)"
            ],
            "text/plain": [
              "<IPython.core.display.Javascript object>"
            ]
          },
          "metadata": {
            "tags": []
          }
        }
      ]
    }
  ]
}